#include "DB-OS/buffer-pool.h"
#include "utils/logger.h"
#include <cassert>
#include <shared_mutex>

// 构造函数：初始化缓冲池
BufferPoolManager::BufferPoolManager(size_t pool_size, std::unique_ptr<Replacer> replacer, DiskManager* disk_manager)
    : pool_size_(pool_size),
      pages_(new Page[pool_size]),           // 分配 pool_size 个 Page 对象
      replacer_(std::move(replacer)),
      disk_manager_(disk_manager) {
  // 初始化每个页为无效状态
  for (size_t i = 0; i < pool_size_; ++i) {
    pages_[i].reset();  // reset() 会设置 page_id=INVALID, pin_count=0, dirty=false, 并清空数据
  }

  // 2. ✅ 初始化 free_list_：把所有帧加入空闲列表
    for (size_t i = 0; i < pool_size_; ++i) {
        free_list_.push_back(static_cast<frame_id_t>(i));
    }
}

// 析构函数：释放资源，刷回所有脏页
BufferPoolManager::~BufferPoolManager() {
  std::lock_guard<std::mutex> lock(latch_);
  // 在销毁前，将所有脏页写回磁盘
  FlushAllPages();
  // 删除页数组
  delete[] pages_;
  pages_ = nullptr;
}

// 获取指定页的指针。如果不在缓冲池中，则从磁盘加载。
Page* BufferPoolManager::FetchPage(page_id_t page_id) {
  std::lock_guard<std::mutex> lock(latch_);  // 加锁保证线程安全

  // 1. 检查页是否已在缓冲池中
  auto iter = page_table_.find(page_id);
  if (iter != page_table_.end()) {
    frame_id_t fid = iter->second;
    Page* page = &pages_[fid];

    // 增加 pin 计数
    page->pin_count();

    // 如果该页之前未被 pin（pin_count == 1），则需从 replacer 中移除
    // 因为被 pin 的页不能被替换器淘汰
    if (page->get_pin_count() == 1) {
      replacer_->Erase(fid);
    }

    return page;
  }

  // 2. 页不在缓冲池中，需要加载或分配新帧

  // 查找一个可用的帧（空闲帧或受害者）
  frame_id_t fid = INVALID_FRAME_ID;

  // ✅ 优先从 free_list_ 分配空闲帧
  if (!free_list_.empty()) {
    fid = free_list_.front();   // 取出第一个空闲帧
    free_list_.pop_front();     // 从空闲列表移除
  }
  // ✅ 如果没有空闲帧，才找一个 victim
  else {
    fid = FindVictimFrame();
    if (fid == INVALID_FRAME_ID) {
      // 缓冲池满且所有帧都被 pin，无法加载
      return nullptr;
    }
  }

  Page* page = &pages_[fid];

  // 3. 如果该帧原来有有效页且是脏页，则写回磁盘
  if (page->get_page_id() != INVALID_PAGE_ID && page->is_dirty()) {
    disk_manager_->write_page(page->get_page_id(), page->get_data());
  }

  // 4. 从磁盘读取目标页数据
  disk_manager_->read_page(page_id, page->get_data());

  // 5. 更新页元数据
  page->set_page_id(page_id);
  page->mark_clean();         // 刚从磁盘读入，不是脏页
  page->pin_count();          // 当前被 pin

  // 6. 更新页表
  page_table_[page_id] = fid;

  // 7. 注意：新加载的页被 pin，不加入 replacer
  // 只有当 UnpinPage 且 pin_count 变为 0 时，才 Insert 到 replacer

  return page;
}

// 释放对页的引用
bool BufferPoolManager::UnpinPage(page_id_t page_id, bool is_dirty) {
  std::lock_guard<std::mutex> lock(latch_);

  auto iter = page_table_.find(page_id);
  if (iter == page_table_.end()) {
    // 页不在缓冲池中
    return false;
  }

  frame_id_t fid = iter->second;
  Page* page = &pages_[fid];

  if (page->get_pin_count() <= 0) {
    // 错误：页未被 pin，不能 unpin
    return false;
  }

  // 标记脏页（如果需要）
  if (is_dirty) {
    page->mark_dirty();
  }

  // 减少 pin 计数
  page->unpin_count();

  // 如果 pin_count 变为 0，表示该页可被替换
  if (page->get_pin_count() == 0) {
    replacer_->Insert(fid);
  }

  return true;
}

// 将指定页写回磁盘
bool BufferPoolManager::FlushPage(page_id_t page_id) {
  std::lock_guard<std::mutex> lock(latch_);

  auto iter = page_table_.find(page_id);
  if (iter == page_table_.end()) {
    // 页不在缓冲池中
    return false;
  }

  frame_id_t fid = iter->second;
  Page* page = &pages_[fid];

  // 无论是否脏，都写回磁盘
  disk_manager_->write_page(page_id, page->get_data());

  // 写回后标记为干净
  page->mark_clean();

  return true;
}

// 将所有脏页写回磁盘
size_t BufferPoolManager::FlushAllPages() {
   std::lock_guard<std::mutex> lock(latch_);

    size_t actual_flushed_count = 0;  // 统计实际刷回的脏页数

    // 遍历 page_table_（所有在缓冲池中的页）
    for (const auto& pair : page_table_) {
        page_id_t page_id = pair.first;
        frame_id_t fid = pair.second;
        Page* page = &pages_[fid];

        // 仅刷回脏页（避免无效IO）
        if (page->is_dirty()) {
            // 调用磁盘管理器写回数据
            if (disk_manager_->write_page(page_id, page->get_data())) {
                page->mark_clean();  // 清除脏标志
                actual_flushed_count++;  // 仅成功写入时计数
                LOG_DEBUG_F("Flushed dirty page %d (frame %d) to disk", page_id, fid);
            } else {
                // 处理磁盘写入失败（如日志报警，避免统计错误）
                LOG_ERROR_F("Failed to flush dirty page %d (frame %d) to disk", page_id, fid);
            }
        }
    }

    return actual_flushed_count;  // 返回实际刷回的脏页数
}

// 分配一个新页
page_id_t BufferPoolManager::NewPage() {
  std::lock_guard<std::mutex> lock(latch_);

  // 1. 向磁盘管理器申请一个新的页 ID
  page_id_t new_page_id = disk_manager_->allocate_page();
  if (new_page_id == INVALID_PAGE_ID) {
    return INVALID_PAGE_ID;
  }

  // 2. 优先从 free_list_ 分配
  frame_id_t fid = INVALID_FRAME_ID;
  if (!free_list_.empty()) {
    fid = free_list_.front();
    free_list_.pop_front();
  } else {
    // 没有空闲帧，才找 victim
    fid = FindVictimFrame();
    if (fid == INVALID_FRAME_ID) {
      // 无法分配帧，释放已申请的页 ID
      disk_manager_->free_page(new_page_id);
      return INVALID_PAGE_ID;
    }
  }

  Page* page = &pages_[fid];
  // 3. 如果原页是脏页，写回磁盘
  if (page->get_page_id() != INVALID_PAGE_ID && page->is_dirty()) {
    disk_manager_->write_page(page->get_page_id(), page->get_data());
  }

  // 4. 初始化新页
  page->set_page_id(new_page_id);
  page->mark_clean();
  page->pin_count();  // 新页被 pin

  // 5. 更新页表
  page_table_[new_page_id] = fid;

  // 6. 不加入 replacer（因为被 pin）

  return new_page_id;
}

// 删除一个页
bool BufferPoolManager::DeletePage(page_id_t page_id) {
  std::lock_guard<std::mutex> lock(latch_);

  // 1. 检查页是否在缓冲池中
  auto iter = page_table_.find(page_id);
  if (iter != page_table_.end()) {
    frame_id_t fid = iter->second;
    Page* page = &pages_[fid];

    // 如果页被 pin，不能删除
    if (page->get_pin_count() > 0) {
      return false;
    }

    // 从 replacer 中移除（如果在候选集中）
    replacer_->Erase(fid);

    // 如果是脏页，写回磁盘
    if (page->is_dirty()) {
      disk_manager_->write_page(page_id, page->get_data());
    }

    // 从页表中删除
    page_table_.erase(iter);

    // 重置页状态
    page->reset();
    replacer_->Insert(fid);  // 将该帧加入空闲列表
  }

  // 2. 释放磁盘上的页空间
  disk_manager_->free_page(page_id);

  return true;
}

// 获取指定页的 pin_count（调试用）
int BufferPoolManager::GetPinCount(page_id_t page_id) const {
  std::lock_guard<std::mutex> lock(latch_);

  auto iter = page_table_.find(page_id);
  if (iter == page_table_.end()) {
    return 0;
  }
  return pages_[iter->second].get_pin_count();
}

// 检查页是否为脏页（调试用）
bool BufferPoolManager::IsDirty(page_id_t page_id) const {
  std::lock_guard<std::mutex> lock(latch_);

  auto iter = page_table_.find(page_id);
  if (iter == page_table_.end()) {
    return false;
  }
  return pages_[iter->second].is_dirty();
}

// 获取缓冲池中已分配的页数（调试用）
size_t BufferPoolManager::GetPageCount() const {
  std::lock_guard<std::mutex> lock(latch_);
  return page_table_.size();
}

// 私有方法：查找页是否在缓冲池中（线程不安全，需外部加锁）
Page* BufferPoolManager::FindPage(page_id_t page_id) {
  auto iter = page_table_.find(page_id);
  if (iter == page_table_.end()) {
    return nullptr;
  }
  return &pages_[iter->second];
}

// 私有方法：选择一个受害者帧
frame_id_t BufferPoolManager::FindVictimFrame() {
  if (!replacer_) {
    LOG_ERROR("replacer_ is null!");
    return INVALID_FRAME_ID;
  }

  frame_id_t victim_id;
  if (replacer_->Victim(&victim_id)) {
    // 找到受害者，清理旧映射
    page_id_t old_page_id = pages_[victim_id].get_page_id();
    if (old_page_id != INVALID_PAGE_ID) {
      page_table_.erase(old_page_id);
    }
    return victim_id;
  }

  return INVALID_FRAME_ID;  // 所有帧都被 pin
}